#pragma once

#include "zen_game_graphics.h"
#include "zen_cache.h"
#include "zen_image.h"
#include <map>

namespace Zen
{
	namespace Game
	{
		struct TextureRect
		{
			float x0{}, y0{}, x1{ 1 }, y1{ 1 };
			bool operator==(TextureRect const& o) const
			{
				return x0 == o.x0 && y0 == o.y0 && x1 == o.x1 && y1 == o.y1;
			}
			bool operator!=(TextureRect const& o) const
			{
				return !(*this == o);
			}
		};

		class TextureLoaderI
		{
		public:
			virtual std::shared_ptr<Image> OnLoadTexture(std::string const& key) = 0;
		};

		class DefaultTextureLoader : public TextureLoaderI
		{
		public:
			virtual std::shared_ptr<Image> OnLoadTexture(std::string const& key) override;
		};

		class TextureCache : public Zen::Cache<std::string, Zen::Graphics::Texture>
		{
		public:
			TextureCache(std::shared_ptr<Zen::Graphics::Engine> engine);

			void SetDelegate(std::shared_ptr<TextureLoaderI> delegate);

			std::shared_ptr<Zen::Graphics::Texture> AddImage(std::string const& key, std::shared_ptr<Image> const& image);

		protected:
			virtual std::shared_ptr<Zen::Graphics::Texture> LoadData(std::string const& key);

			std::shared_ptr<TextureLoaderI> mDelegate;
			std::shared_ptr<Zen::Graphics::Engine> mEngine;
		};

		class BuiltinImages
		{
		public:
			static BuiltinImages* Only();

		public:
			std::shared_ptr<Zen::Image> CreateWithAlphaChannel(EPixel destFormat, size_t w_h, void const* alphas);
			std::shared_ptr<Zen::Image> CreateWhiteTexture(size_t w_h);
			std::shared_ptr<Zen::Image> CreateBlackTexture(size_t w_h);

			std::shared_ptr<Zen::Image> CreateParticleTexture(EPixel format, size_t w_h);
			std::shared_ptr<Zen::Image> CreateCrossTexture(EPixel format, size_t w_h);
			std::shared_ptr<Zen::Image> CreateCircleTexture(EPixel format, size_t w_h);
		};

		class TextureFrameI
		{
		public:
			virtual ~TextureFrameI() = default;
			virtual std::shared_ptr<Zen::Graphics::Texture> GetTexture() = 0;
			virtual Point2 ProjectCoord(Point2 point) = 0;
			virtual std::vector<Point2> ProjectCoords(std::vector<Point2> points) = 0;
			// default coords is the 4 corner of the rect area in order: lt,rt,lb,rb
			// this buffer will be created or updated when get it.
			virtual std::shared_ptr<Zen::Graphics::Buffer> GetDefaultCoordBuffer() = 0;

			int GetVersion();
			void SetDirty();

		private:
			int mVersion{};
		};

		class TextureFrame : public TextureFrameI
		{
		public:
			virtual std::shared_ptr<Zen::Graphics::Texture> GetTexture() override;
			virtual Point2 ProjectCoord(Point2 point) override;
			virtual std::vector<Point2> ProjectCoords(std::vector<Point2> points) override;
			virtual std::shared_ptr<Zen::Graphics::Buffer> GetDefaultCoordBuffer() override;

			void SetTexture(std::shared_ptr<Zen::Graphics::Texture> texture);

		protected:
			std::shared_ptr<Zen::Graphics::Texture> mTexture;
			std::shared_ptr<Zen::Graphics::Buffer> mDefaultCoordBuffer;
			int mBufferVerion{};
		};

		class TextureFrameInRect : public TextureFrame
		{
		public:
			virtual Point2 ProjectCoord(Point2 point) override;
			virtual std::vector<Point2> ProjectCoords(std::vector<Point2> points) override;
			virtual std::shared_ptr<Zen::Graphics::Buffer> GetDefaultCoordBuffer() override;

			void SetTexture(std::shared_ptr<Zen::Graphics::Texture> texture, TextureRect rect = {}, bool flipX = false, bool flipY = false);
			TextureRect const& GetTextureRect() const;
			void SetFlip(bool x, bool y);
			void SetFlipX(bool);
			void SetFlipY(bool);
			bool IsFlipX() const;
			bool IsFlipY() const;

		protected:
			TextureRect mTextureRect{};
			bool mFlipX{};
			bool mFlipY{};
		};
		//class TextureFrameI
	} // namespace Game
} // namespace Zen
